/*
Copyright 2007 Raymond Giorgi

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

   http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
 */

package edu.pitt.CS2510.NodeActions;

import java.util.ArrayList;

import edu.pitt.CS2510.Coordinate;
import edu.pitt.CS2510.Grids.Grid;
import edu.pitt.CS2510.Grids.GridPiece;
import edu.pitt.CS2510.Messages.AttackMessage;
import edu.pitt.CS2510.Messages.DoneMessage;
import edu.pitt.CS2510.Messages.Message;
import edu.pitt.CS2510.Messages.QuitMessage;
import edu.pitt.CS2510.Messages.RequestMessage;
import edu.pitt.CS2510.Messages.SetTypeMessage;
import edu.pitt.CS2510.Messages.UpdateGridMessage;
import edu.pitt.CS2510.Nodes.WorkerNode;

/**
 * The actions that a supervisor can take given. This class also handles the
 * sending of messages to the Central / Grunt nodes
 * 
 * @author NoOne
 * 
 * 
 */
public class SupervisorActions implements NodeActionsInterface {
	private final ArrayList<Integer> helpers = new ArrayList<Integer>();
	private final Coordinate enemyCoordinate = null;
	protected WorkerNode parent = null;

	/**
	 * The node to whom this class's instantiation belongs to
	 * 
	 * @param parent
	 */
	public SupervisorActions(WorkerNode parent) {
		this.parent = parent;
	}

	/**
	 * 
	 * @return the coordinate of the enemy being attacked
	 */
	public Coordinate getEnemyCoordinate() {
		return enemyCoordinate;
	}

	/**
	 * 
	 * @return a list of the grunt nodes assigned to this Supervisor
	 */
	public ArrayList<Integer> getHelpers() {
		return helpers;
	}

	/**
	 * Take this action when given an attack message Position itself next to the
	 * enemy node.
	 */
	@Override
	public void processAttackMessage(Coordinate co) {
		parent.getId();
		final Grid grid = parent.getGrid();
		final ArrayList<GridPiece> temp = grid.getNeighbors(co.getX(), co
				.getY());
		final GridPiece piece = (GridPiece) temp.get(0);
		piece.setUsed(true);
		piece.setTeam(GridPiece.Friend);
		parent.setCoordinate(new Coordinate(piece.getX(), piece.getY()));
		final ArrayList<Coordinate> list = new ArrayList<Coordinate>();
		list.add(new Coordinate(piece.getX(), piece.getY()));
		sendUpdateMessage(0, list);
		parent.setEnemy(co);
	}

	/**
	 * If the system is performing correctly, this method should not be called.
	 * Useful in debugging
	 */
	@Override
	public void processDoneMessage(int from, ArrayList<Integer> nodes) {
		// TODO Auto-generated method stub

	}

	/**
	 * If the system is performing correctly, this method should not be called.
	 * Useful in debugging
	 */
	@Override
	public void processQuitMessage() {
		// TODO Auto-generated method stub

	}

	/**
	 * If the system is performing correctly, this method should not be called.
	 * Useful in debugging
	 */
	@Override
	public void processRequestMessage(int from, int no) {
		// TODO Auto-generated method stub

	}

	/**
	 * When given a supply message, figure out where the supplied nodes should
	 * be placed in relation to the enemy.
	 */
	public void processSupplyMessage(ArrayList<Integer> list) {
		ArrayList<Coordinate> coordinates = new ArrayList<Coordinate>();
		for (final GridPiece piece : parent.getGrid().getNeighbors(
				parent.getEnemy().getX(), parent.getEnemy().getY())) {
			if (!piece.isUsed() && !list.isEmpty()) {
				final int node = (list.remove(0)).intValue();
				sendSetTypeMessage(node, WorkerNode.Grunt);
				sendAttackMessage(node, new Coordinate(piece.getX(), piece
						.getY()));
				coordinates.add(new Coordinate(piece.getX(), piece.getY()));
				parent.addGrunt(node);
			}
		}
		if (!list.isEmpty()) {
			sendQuitMessages(list);
		}
		if (coordinates.size() > 0) {
			this.sendUpdateMessage(0, coordinates);
		}
	}

	/**
	 * Update the grid with the list of coordinates
	 */
	public void processUpdateMessage(ArrayList<Coordinate> list, int from) {
		updateGrid(list);
	}

	/**
	 * Change the type of the node back to its default state
	 */
	protected void release() {
		parent.setType(-1);
	}

	/**
	 * Send a message to a node telling it to attack a specific coordinate
	 * 
	 * @param to
	 * @param co
	 */
	public void sendAttackMessage(int to, Coordinate co) {
		parent.sendMessage(new AttackMessage(to, parent.getId(), co));
	}

	/**
	 * Send a message to the central telling it that this node has finished and
	 * the Supervisor and Grunts should be put back in the idle list
	 */
	public void sendDoneMessage() {
		parent.addGrunt(parent.getId());
		parent.sendMessage(new DoneMessage(Message.CENTRAL_NODE,
				parent.getId(), parent.getHelpers()));
	}

	/**
	 * Tell the Grunt nodes this node is supervising to quit attacking and put
	 * themselves in an 'idle' state
	 * 
	 * @param list
	 */
	public void sendQuitMessages(ArrayList<Integer> list) {
		for (Integer integer : list) {
			final int node = integer;
			parent.sendMessage(new QuitMessage(node, parent.getId()));
		}
	}

	/**
	 * Request more nodes from the Central
	 * 
	 * @param no
	 */
	public void sendRequestMessage(int no) {
		parent.sendMessage(new RequestMessage(Message.CENTRAL_NODE, parent
				.getId(), no));
	}

	/**
	 * Tell each of the grunt nodes that they are grunt nodes
	 * 
	 * @param node
	 * @param type
	 */
	public void sendSetTypeMessage(int node, int type) {
		parent.sendMessage(new SetTypeMessage(node, parent.getId(), type));
	}

	/**
	 * Send each of the Grunt nodes an updated list of coordinates. Not ever
	 * used. Here for historic reasons
	 * 
	 * @param node
	 * @param list
	 */
	public void sendUpdateMessage(int node, ArrayList<Coordinate> list) {
		parent.sendMessage(new UpdateGridMessage(node, parent.getId(), list));
	}

	/**
	 * If the system is performing correctly, this method should not be called.
	 * Useful in debugging
	 */
	@Override
	public void updateGrid(ArrayList<Coordinate> coordinates) {
		// TODO Auto-generated method stub

	}
}
